package com.moorror.codegenvel.utils;

import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import org.apache.commons.collections4.CollectionUtils;

import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;

/**
 *
 */
public class MMapUtils {

    /**
     * @param map
     * @param key
     * @param data
     * @param <T>
     * @param <KT>
     */
    public static <T, KT> void fillMapList(Map<KT, List<T>> map, KT key, T data) {
        List<T> dList = map.get(key);
        if (CollectionUtils.isEmpty(dList)) {
            dList = Lists.newArrayList();
            map.put(key, dList);
        }
        dList.add(data);
    }

    public static <T, KT> void fillMap(Map<KT, T> filledMap, Function<T, KT> keyFun, List<T> dataList) {
        if (null == filledMap || CollectionUtils.isEmpty(dataList)) {
            return;
        }
        dataList.forEach(t -> {
            filledMap.put(keyFun.apply(t), t);
        });
    }

    public static <T, KT> Map<KT, T> fillMap(Function<T, KT> keyFun, List<T> dataList) {
        Map<KT, T> filledMap = Maps.newHashMap();
        if (CollectionUtils.isEmpty(dataList)) {
            return filledMap;
        }
        dataList.forEach(t -> {
            filledMap.put(keyFun.apply(t), t);
        });
        return filledMap;
    }

    public static <T, KT> void fillListMap(Map<KT, List<T>> filledMap, Function<T, KT> keyFun, List<T> dataList) {
        if (null == filledMap || CollectionUtils.isEmpty(dataList)) {
            return;
        }
        dataList.forEach(t -> {
            KT kt = keyFun.apply(t);
            List<T> listValue = filledMap.get(kt);
            if (CollectionUtils.isEmpty(listValue)) {
                listValue = Lists.newArrayList();
                filledMap.put(kt, listValue);
            }
            listValue.add(t);
        });
    }

    public static <T, KT> Map<KT, List<T>> fillListMap(Function<T, KT> keyFun, List<T> dataList) {
        Map<KT, List<T>> filledMap = Maps.newHashMap();
        if (null == filledMap || CollectionUtils.isEmpty(dataList)) {
            return filledMap;
        }
        dataList.forEach(t -> {
            KT kt = keyFun.apply(t);
            List<T> listValue = filledMap.get(kt);
            if (CollectionUtils.isEmpty(listValue)) {
                listValue = Lists.newArrayList();
                filledMap.put(kt, listValue);
            }
            listValue.add(t);
        });
        return filledMap;
    }

    public static <T, KT> Map<KT, List<T>> fillListMap(Function<T, KT> keyFun, List<T> dataList, Predicate<T> test) {
        Map<KT, List<T>> filledMap = Maps.newHashMap();
        if (null == filledMap || CollectionUtils.isEmpty(dataList)) {
            return filledMap;
        }
        dataList.forEach(t -> {
            if (!test.test(t)) {
                return;
            }
            KT kt = keyFun.apply(t);
            List<T> listValue = filledMap.get(kt);
            if (CollectionUtils.isEmpty(listValue)) {
                listValue = Lists.newArrayList();
                filledMap.put(kt, listValue);
            }
            listValue.add(t);
        });
        return filledMap;
    }

    /**
     * @param keyOneFun
     * @param keySecondFun
     * @param values
     * @param <V>
     * @param <KO>
     * @param <KT>
     * @return
     */
    public static <V, KO, KT> Map<KO, Map<KT, V>> genMapMap(Function<V, KO> keyOneFun, Function<V, KT> keySecondFun, List<V> values) {
        Map<KO, Map<KT, V>> keyOneMap = Maps.newHashMap();
        if (CollectionUtils.isEmpty(values)) {
            return keyOneMap;
        }
        Map<KT, V> keySecondMap;
        for (V v : values) {
            keySecondMap = keyOneMap.get(keyOneFun.apply(v));
            if (null == keySecondMap) {
                keySecondMap = Maps.newHashMap();
                keyOneMap.put(keyOneFun.apply(v), keySecondMap);
            }
            keySecondMap.put(keySecondFun.apply(v), v);
        }
        return keyOneMap;
    }

    /**
     * @param keyOneFun
     * @param keySecondFun
     * @param keyThirdFun
     * @param transferFun
     * @param values
     * @param keyOneSet
     * @param keyTwoSet
     * @param keyThirdSet
     * @param <V>
     * @param <KO>
     * @param <KT>
     * @param <KR>
     * @param <VT>
     * @return
     */
    public static <V, KO, KT, KR, VT> Map<KO, Map<KT, Map<KR, VT>>> genMapMapMap(Function<V, KO> keyOneFun, Function<V, KT> keySecondFun, Function<V, KR> keyThirdFun, Function<V, VT> transferFun,
                                                                                 List<V> values, Set<KO> keyOneSet, Set<KT> keyTwoSet, Set<KR> keyThirdSet) {
        Map<KO, Map<KT, Map<KR, VT>>> keyOneMap = Maps.newHashMap();
        if (CollectionUtils.isEmpty(values)) {
            return keyOneMap;
        }
        Map<KT, Map<KR, VT>> secondMap;
        Map<KR, VT> thirdMap;
        KO ko;
        KT kt;
        KR kr;
        for (V v : values) {
            ko = keyOneFun.apply(v);
            keyOneSet.add(ko);
            secondMap = keyOneMap.get(ko);
            if (null == secondMap) {
                secondMap = Maps.newHashMap();
                keyOneMap.put(ko, secondMap);
            }
            kt = keySecondFun.apply(v);
            keyTwoSet.add(kt);
            thirdMap = secondMap.get(kt);
            if (null == thirdMap) {
                thirdMap = Maps.newHashMap();
                secondMap.put(kt, thirdMap);
            }
            kr = keyThirdFun.apply(v);
            keyThirdSet.add(kr);
            thirdMap.put(kr, transferFun.apply(v));
        }
        return keyOneMap;
    }

    public static <V, KO, KT, VL> Map<KO, Map<KT, List<VL>>> genMapMapList(Function<V, KO> keyOneFun, Function<V, KT> keySecondFun, Function<V, VL> fvl, List<V> values) {
        Map<KO, Map<KT, List<VL>>> keyOneMap = Maps.newHashMap();
        if (CollectionUtils.isEmpty(values)) {
            return keyOneMap;
        }
        Map<KT, List<VL>> secondMap;
        List<VL> dListT;
        for (V v : values) {
            secondMap = keyOneMap.get(keyOneFun.apply(v));
            if (null == secondMap) {
                secondMap = Maps.newHashMap();
                keyOneMap.put(keyOneFun.apply(v), secondMap);
            }
            dListT = secondMap.get(keySecondFun.apply(v));
            if (null == dListT) {
                dListT = Lists.newArrayList();
                secondMap.put(keySecondFun.apply(v), dListT);
            }
            dListT.add(fvl.apply(v));
        }
        return keyOneMap;
    }

    public static <KT, VT> List<VT> fetchMap(Set<KT> keys, Map<KT, VT> valueMap, VT defaultValue) {
        List<VT> re = Lists.newArrayList();
        if (null == keys || keys.size() <= 0) {
            return re;
        }
        for (KT key : keys) {
            VT vt = valueMap.get(key);
            if (null != vt) {
                re.add(vt);
            } else {
                re.add(defaultValue);
            }
        }
        return re;
    }

    public static <T, K> Set<T> valuesAll(Map<K, Set<T>> typeShowMap) {
        Set<T> re = Sets.newHashSet();
        for (Map.Entry<K, Set<T>> entry : typeShowMap.entrySet()) {
            if (null != entry.getValue()) {
                re.addAll(entry.getValue());
            }
        }
        return re;
    }

    public static <T, K> List<T> fetchList(Map<K, T> valueMap, List<K> ks) {
        List<T> re = Lists.newArrayList();
        for (K k : ks) {
            T t = valueMap.get(k);
            if (null != t) {
                re.add(t);
            }
            return re;
        }
        return re;
    }

    /**
     * 从哈希表查找到 key 对应的 value，然后进一步处理
     * key 为 null 时, 不处理
     * 注意，如果查找到的 value 为 null 时，不进行处理
     *
     * @param map      哈希表
     * @param key      key
     * @param consumer 进一步处理的逻辑
     */
    public static <K, V> void findAndThen(Map<K, V> map, K key, Consumer<V> consumer) {
        if (null == key || isEmpty(map)) {
            return;
        }
        V value = map.get(key);
        if (value == null) {
            return;
        }
        consumer.accept(value);
    }

    public static boolean isEmpty(Map<?, ?> map) {
        return null == map || map.isEmpty();
    }

    public static <K, V> Map<K, Integer> keySortIndex(Map<K, V> map) {
        if (isEmpty(map)) {
            return (Map) Maps.newTreeMap();
        }
        Map<K, Integer> sortIndexMap = (Map) Maps.newTreeMap();
        for (Map.Entry<K, V> entry : map.entrySet()) {
            sortIndexMap.put(entry.getKey(), 0);
        }
        int index = 1;
        for (Map.Entry<K, Integer> entry : sortIndexMap.entrySet()) {
            sortIndexMap.put(entry.getKey(), index);
            index++;
        }
        return sortIndexMap;
    }
}
